package hu.dobrosi.dinkycam.j2me;

import hu.dobrosi.dinkycam.gwt.client.StaticUtil;
import hu.dobrosi.dinkycam.j2me.forms.AdvancedSettingsForm;
import hu.dobrosi.dinkycam.j2me.forms.BluetoothDeviceList;
import hu.dobrosi.dinkycam.j2me.forms.BluetoothDeviceListResponseHandler;
import hu.dobrosi.dinkycam.j2me.forms.LogForm;
import hu.dobrosi.dinkycam.j2me.forms.SettingsForm;
import hu.dobrosi.dinkycam.j2me.screens.CaptureCanvas;
import hu.dobrosi.dinkycam.j2me.screens.InfoAlert;
import hu.dobrosi.dinkycam.j2me.screens.IntroCanvas;
import hu.dobrosi.dinkycam.j2me.util.Preferences;
import hu.dobrosi.j2me.io.ImageReader;
import hu.dobrosi.util.StringUtil;
import hu.dobrosi.util.io.HttpClient;
import hu.dobrosi.util.io.gps.GPSReader;
import hu.dobrosi.util.log.SysoutLogger;
import hu.dobrosi.util.log.TextFieldLogger;

import java.io.IOException;
import java.util.Date;

import javax.microedition.io.ConnectionNotFoundException;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.List;
import javax.microedition.midlet.MIDletStateChangeException;
import javax.microedition.rms.RecordStoreException;

public class DinkycamApp {

	/*
	 * POST /dinkycam/HttpStreamServlet HTTP/1.0 Content-Length:
	 * 1000000000000000
	 * 
	 * POST /dinkycam/ HTTP/1.0 Content-Length: 100
	 * 
	 * GET / HTTP/1.0 Content-Length: 100
	 */
	private static final short ROUND = 4;
	private static final int GPS_READ_LINE_NUMBER = 20;
	private InfoAlert infoScreen;
	private CaptureCanvas captureCanvas;
	private SettingsForm settingsForm;
	private AdvancedSettingsForm advancedSettingsForm;
	private LogForm logForm;
	private Display display;
	public static DinkycamMIDlet midlet;
	private static Preferences preferences;
	private BluetoothDeviceList ownerBluetoothDeviceList;
	private BluetoothDeviceList gpsBluetoothDeviceList;
	private GPSReader gpsReader;
	private boolean pIsMoving;

	private Date lastReadDate = new Date();
	private int periodIndex = 0;
	private HttpClient httpClient = new HttpClient();

	public DinkycamApp(DinkycamMIDlet midlet) {
		DinkycamApp.midlet = midlet;
		try {
			preferences = new Preferences("dinkycamStore");
		} catch (RecordStoreException e) {
			showError(this, "DinkycamApp(DinkycamMIDlet)", e);
		}
		display = Display.getDisplay(midlet);
		showIntro();
		sleep(3000);
		buildGUI();
		if (hasValidOpenId()) {
			showCapture();
		} else {
			showSettings();
		}
	}

	private void buildGUI() {
		logForm = new LogForm(this, "_Logging_____");
		if (!isEmulator()) {
			StringUtil.logger = new TextFieldLogger(logForm.getLogTextField());
		} else {
			StringUtil.logger = new SysoutLogger();
		}
		infoScreen = new InfoAlert();
		settingsForm = new SettingsForm(this, Labels.TITLE_SETTINGS.toString());
		advancedSettingsForm = new AdvancedSettingsForm(this,
				Labels.TITLE_ADVANCED_SETTINGS.toString());
		captureCanvas = new CaptureCanvas(this);
		ownerBluetoothDeviceList = new BluetoothDeviceList(List.MULTIPLE, this,
				"Bluetooth", getSettingsForm().getOwnerDeviceTextField(),
				new BluetoothDeviceListResponseHandler() {
					public void handle() {
						findOwnerDevice();
					}
				});
		gpsBluetoothDeviceList = new BluetoothDeviceList(List.EXCLUSIVE, this,
				"Bluetooth", getSettingsForm().getGpsDeviceTextField(), null);
		StringUtil.logger.println(System.getProperty("microedition.platform")
				+ " " + System.getProperty("microedition.locale"));
	}

	/**
	 * @param imageData
	 * @return true if is moving
	 * @throws IOException
	 */
	public synchronized boolean capture(final byte[] imageData)
			throws IOException {
		StringUtil.logger.clear();
		boolean isMoving = false;
		boolean hasGps = false;
		periodIndex++;
		if (useGps()) {
			try {
				hasGps = hasGps(gpsReader);
				gpsReader.readDevice(GPS_READ_LINE_NUMBER);
				isMoving = gpsReader.isMoving();
				if (hasGps) {
					if (pIsMoving && !isMoving) {
						// send the first not moving signal (moving stopped)
						long time = gpsReader.getFix().getTime().getTime();
						time -= getAdvancedSettingsForm()
								.getGpsLimitTimeTextField().getValue() * 60 * 1000;
						sendPositionData(time, getAdvancedSettingsForm()
								.getGpsLimitTimeTextField().getValue());
					}
				}
			} catch (IOException e) {
			}
		}
		if (imageData != null
				|| (hasGps && (gpsReader.isMoving() || checkSleepingTime()))) {
			lastReadDate = new Date();
			if (imageData != null) {
				sendImageData(imageData);
			} else {
				sendPositionData(gpsReader.getFix().getTime().getTime(), -1);
			}
		}
		try {
			String log = getLog();
			StringUtil.logger.println(log);
			captureCanvas.setText(log);
		} catch (Exception e) {
		}
		return pIsMoving = isMoving;
	}

	private String getLog() {
		String str = "";
		str = "OpenId:" + settingsForm.getOpenIdTextField().getString();
		str += "\nLat:"
				+ hu.dobrosi.util.Math.round(gpsReader.getLatitude(), ROUND);
		str += " Lon:"
				+ hu.dobrosi.util.Math.round(gpsReader.getLongitude(), ROUND);
		str += " Alt:"
				+ hu.dobrosi.util.Math.round(gpsReader.getAltitude(), ROUND);
		if (gpsReader != null) {
			str += "\nGPS:"
					+ (gpsReader.hasSignal() ? "1" : "0")
					+ ","
					+ (gpsReader.getLastException() != null ? gpsReader
							.getLastException().toString() : "");
			if (lastReadDate != null) {
				str += "\nLast:" + new Date(lastReadDate.getTime());
				str += "\nTime:" + gpsReader.getFix().getTime();
				str += " Next:"
						+ (lastReadDate.getTime()
								+ getAdvancedSettingsForm()
										.getGpsStandbySleepTimeTextField()
										.getValue() * 60 * 1000 - System
								.currentTimeMillis()) / 1000;
			}
		}
		return str;
	}

	private boolean hasValidOpenId() {
		return settingsForm.getOpenIdTextField().getString().length() == 8;
	}

	private void sendImageData(byte[] imageData) throws IOException {
		String url;
		long time = gpsReader.getFix().getTime().getTime();
		String fileName = "" + time + ".jpg";
		httpClient.postFile(StaticUtil.WEBAPP_HOST, url = "/saveFile.do"
				+ buildQuery(false, null, time, -1), fileName, imageData);
		StringUtil.logger.println(url);
	}

	private void sendPositionData(long time, int limitTime) throws IOException {
		String url;
		httpClient.get(StaticUtil.WEBAPP_HOST, url = "/savePosition.do"
				+ buildQuery(true, null, time, limitTime));
		StringUtil.logger.println(url);
	}

	private void sendException(Exception exc) throws IOException {
		String url;
		httpClient.get(StaticUtil.WEBAPP_HOST, url = "/saveException.do"
				+ buildExceptionQuery(exc));
		StringUtil.logger.println(url);
	}

	private boolean checkSleepingTime() {
		return System.currentTimeMillis() - lastReadDate.getTime() >= getAdvancedSettingsForm()
				.getGpsStandbySleepTimeTextField().getValue() * 60 * 1000;
	}

	private String buildQuery(boolean hasGps, byte[] imageData, long time,
			int limitTime) {
		String query = buildQuery();
		if (hasGps) {
			query += "&"
					+ StaticUtil.P_LAT
					+ "="
					+ hu.dobrosi.util.Math
							.round(gpsReader.getLatitude(), ROUND);
			query += "&"
					+ StaticUtil.P_LON
					+ "="
					+ hu.dobrosi.util.Math.round(gpsReader.getLongitude(),
							ROUND);
			query += "&"
					+ StaticUtil.P_ALT
					+ "="
					+ ((long) hu.dobrosi.util.Math.round(
							gpsReader.getAltitude(), 0));
			query += "&" + StaticUtil.P_TIME + "=" + time;
			query += "&" + StaticUtil.P_VELOCITY + "="
					+ gpsReader.getVelocity();
			query += "&" + StaticUtil.P_ISMOVING + "=" + gpsReader.isMoving();
			if (limitTime > 0) {
				query += "&" + StaticUtil.P_LIMIT_TIME + "=" + limitTime;
			}
			if (gpsReader.isMoving()) {
				if (!pIsMoving) {
					if (!findOwnerDevice()) {
						query += "&" + StaticUtil.P_COMMAND_WARNING + "="
								+ true;
						StringUtil.logger.println("warning!");
					}
				}
			}
		} else {
			query += "&" + StaticUtil.P_TIME + "=" + System.currentTimeMillis();
		}
		return query;
	}

	private String buildExceptionQuery(Exception exc) {
		String query = buildQuery();
		query += "&" + StaticUtil.P_CLIENT_VERSION + "=" + StaticUtil.VERSION_MIDLET;
		return null;
	}

	private String buildQuery() {
		String query = "";
		query += "?" + StaticUtil.P_OPEN_ID + "="
				+ settingsForm.getOpenIdTextField().getString();
		query += "&" + StaticUtil.P_CLIENT_VERSION + "=" + StaticUtil.VERSION_MIDLET;
		String signal = System.getProperty("com.nokia.mid.networksignal");
		if(signal != null) {
			query += "&" + StaticUtil.P_NETWORK_SIGNAL + "=" + signal;
			
		}
		String batteryLevel = System.getProperty("com.nokia.mid.batterylevel");
		if (batteryLevel != null) {
			query += "&" + StaticUtil.P_BATTERY_LEVEL + "=" + batteryLevel;

		}
		return query;
	}

	public boolean findOwnerDevice() {
		String ownerDevice = getSettingsForm().getOwnerDeviceTextField()
				.getString();
		if (ownerDevice != null && !ownerDevice.equals("")
				&& ownerDevice.length() == 12) {
			StringUtil.logger.println("findOwnerDevice");

			String url = StaticUtil.CONNSTR_GPS_PRE + ownerDevice
					+ StaticUtil.CONNSTR_GPS_POST;
			try {
				javax.microedition.io.Connector.open(url);
			} catch (Exception exception) {
				return false;
			}
		}
		return true;
	}

	public static Preferences getPreferences() {
		return preferences;
	}

	public void openGps() {
		GPSReader.MIN_DELTA_FIX = getAdvancedSettingsForm()
				.getGpsLimitTimeTextField().getValue() * 60 * 1000;
		try {
			// this will throw an exception if JSR-179 is missing
			Class.forName("javax.microedition.location.Location");
			gpsReader = new GPSReader(null);
		} catch (Exception e) {
			String url = StaticUtil.CONNSTR_GPS_PRE
					+ settingsForm.getGpsDeviceTextField().getString()
					+ StaticUtil.CONNSTR_GPS_POST;
			gpsReader = new GPSReader(url);
		}
	}

	public boolean hasGps(GPSReader gps) {
		return gps != null && useGps();
	}

	public SettingsForm getSettingsForm() {
		return settingsForm;
	}

	public boolean useGps() {
		return !getSettingsForm().getGpsDeviceTextField().getString()
				.equals("");
	}

	private boolean useCamera() {
		return !settingsForm.getCamSleepTextField().getString().startsWith("-");
	}

	public static boolean isEmulator() {
		return System.getProperty("microedition.platform").startsWith("Sun");
	}

	private void showIntro() {
		Image img = null;
		try {
			img = ImageReader.getImage("http://www.dinkycam.com/logo_m.png");
		} catch (IOException e) {
		}
		display.setCurrent(new IntroCanvas(img));
	}

	public void showGpsBluetoothSettings() {
		gpsBluetoothDeviceList.discovery();
		display.setCurrent(gpsBluetoothDeviceList);
	}

	public void showOwnerBluetoothSettings() {
		ownerBluetoothDeviceList.discovery();
		display.setCurrent(ownerBluetoothDeviceList);
	}

	public void showSettings() {
		captureCanvas.cancel();
		display.setCurrent(settingsForm);
	}

	public void showAdvancedSettings() {
		display.setCurrent(advancedSettingsForm);
	}

	public void showCapture() {
		if (hasValidOpenId()) {
			display.setCurrent(captureCanvas);

			if (useCamera()) {
				captureCanvas.startPlayer();
				captureCanvas.startCamTimer();
			} else {
				captureCanvas.stopPlayer();
			}

			if (useGps()) {
				openGps();
				captureCanvas.startGpsTimer();
			}
		} else {
			showError("Not valid openId!");
		}
	}

	public void showLog() {
		display.setCurrent(logForm);
	}

	public void showError(Object source, String string, Exception e) {
		String str = source.getClass().getName() + "#" + string + " " + e;
		StringUtil.logger.clear();
		StringUtil.logger.println(str);
		showError(str);
	}

	public void exit(boolean update) {
		try {
			midlet.destroyApp(false);

			if (update) {
				new Thread() {
					public void run() {
						try {
							midlet.platformRequest(StaticUtil.MIDLETUPDATE_URL);
						} catch (ConnectionNotFoundException e) {
							showError(this, "exit", e);
						}
					}
				}.start();
			}
		} catch (MIDletStateChangeException e) {
			showError(this, "exit", e);
		}
		midlet.notifyDestroyed();
	}

	public void exit() {
		exit(false);
	}

	public void showError(String messageString) {
		infoScreen.showError(messageString, display);
	}

	public void showInfo(String messageString) {
		infoScreen.showInfo(messageString, display);
	}

	public void sleep(long millis) {
		try {
			Thread.sleep(millis);
		} catch (InterruptedException e) {
			StringUtil.logger.clear();
			StringUtil.logger.print(e);
		}
	}

	/**
	 * @return the advancedSettingsForm
	 */
	public AdvancedSettingsForm getAdvancedSettingsForm() {
		return advancedSettingsForm;
	}

}
